//Copyright (C) 2012 Chirila Alexandru
//This file is part of Mirror FTP
//
//Mirror FTP is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.
//
//Mirror FTP is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.
//
//You should have received a copy of the GNU General Public License
//along with Mirror FTP.  If not, see <http://www.gnu.org/licenses/>.


#include "view.h"

view::view()
:prog_ok_btn(Gtk::Stock::OK),
 prog_cancel_btn(Gtk::Stock::CANCEL),

 info_yes_btn(Gtk::Stock::YES),
 info_no_btn(Gtk::Stock::NO),
 info_save_btn(Gtk::Stock::SAVE),

 about_server_btn(Gtk::Stock::OK),

 active_radio("Active"),
 passive_radio("Passive"),
 normal_list_radio("Normal"),
 machine_list_radio("Machine"),

 h_lbl("Server"),
 u_lbl("User"),
 p_lbl("Pass"),

 prog_file_lbl("Downloading file: "),
 prog_speed_lbl("Download speed: "),
 prog_time_lbl("Elapsed time: "),
 prog_eta_lbl("Estimated time:"),

 info_msg_lbl("You have canceled the operation, do you wish to continue the recursive download?"),

 main_box(Gtk::ORIENTATION_VERTICAL),
 ctrl_box(Gtk::ORIENTATION_VERTICAL),
 host_box(Gtk::ORIENTATION_VERTICAL),
 data_box(Gtk::ORIENTATION_VERTICAL),
 list_box(Gtk::ORIENTATION_VERTICAL),
 refr_box(Gtk::ORIENTATION_VERTICAL),
 server_box(Gtk::ORIENTATION_HORIZONTAL),

 main_horz(Gtk::ORIENTATION_VERTICAL),
 main_vert(Gtk::ORIENTATION_HORIZONTAL),

 refr_rate_adj(Gtk::Adjustment::create(0,0,100000)),
 refr_rate_btn(refr_rate_adj),
 time_out_adj(Gtk::Adjustment::create(0,0,1000)),
 time_out_btn(time_out_adj)
{
	path="";
	conn.set_observer(this);
	draw_toolbar();
	prep_view();
	draw_view();
	draw_dialogs();
	connect_signals();
	show_view();
}

view::~view(){
	///Close all connection when view is destroyed.

	conn.closeAll();
	files.clear();
}


//Signals / Events =================================================================
void view::btn_down(){
	///event - on click button download
	if (path==""){
		Gtk::MessageDialog msg("First connect to server",1,Gtk::MESSAGE_ERROR);
		msg.run();
		return;
	}
	if (!conn.is_connected()){
		append_status(":ERR - Connection timed out from server\n");
		return;
	}

	Glib::RefPtr<Gtk::TreeSelection> ref = files.get_selection();
	Gtk::TreeModel::iterator iter = ref->get_selected();
	Glib::ustring x;
	char data_mode=passive_radio.get_active()?'P':'A';

	if(iter){
		Gtk::TreeModel::Row row = *iter;
		if (row[files.type]=="f"){

			prog.show();

			prog_ok_btn.set_sensitive(false);
			prog_cancel_btn.set_sensitive(true);
			set_sensitive(false);

			x=row[files.name];

			conn.send_receive(data_mode,row[files.size],"RETR",x.c_str(),x.c_str(),'I');

			set_sensitive(true);
			prog_ok_btn.set_sensitive(true);
			prog_cancel_btn.set_sensitive(false);
		}
	}
}


void view::btn_load(){
	///event - on click button download all

	if (path==""){
		Gtk::MessageDialog msg("First connect to server",1,Gtk::MESSAGE_ERROR);
		msg.run();
		return;
	}
	if (!conn.is_connected()){
		append_status(":ERR - Connection timed out from server\n");
		return;
	}
	//	if (!conn.check_completed()){
	//		Gtk::MessageDialog msg("Unable to do that right now",1,Gtk::MESSAGE_ERROR);
	//		msg.run();
	//		return;
	//	}

	char data_mode=passive_radio.get_active()?'P':'A';
	char list_mode=machine_list_radio.get_active()?'M':'L';

	set_sensitive(false);
	prog.show();
	prog_ok_btn.set_sensitive(false);
	prog_cancel_btn.set_sensitive(true);

	conn.set_refresh_rate(refr_rate_adj->get_value());
	conn.set_time_out(time_out_adj->get_value());
	conn.download_all(data_mode,list_mode,path);

	set_sensitive(true);
	prog_ok_btn.set_sensitive(true);
	prog_cancel_btn.set_sensitive(false);

	if (!conn.check_completed()){
		info.show();
	}
}

void view::btn_connect(){
	///event - on click button connect
	///		 - connects to inputed ftp server

	Glib::ustring x,y;
	info.hide();
	clear_status();
	x=server_entry.get_text();
	if (x.length()!=0){
		conn.set_address(x);
		if (conn.resolve()!=0)
			return;
	}
	else{
		append_status("ERR : Enter host name\n");
		return;
	}

	x=port_entry.get_text();
	if (x.length()==0){
		append_status(": Port not specified, switching to default 21\n");
		x="21";
	}
	if (conn.connect(atoi(x.c_str()))!=0)
		return;

	x=user_entry.get_text();
	if (x.length()==0){
		append_status(": User not specified, switching to default \"anonymous\"\n");
		x="anonymous";
	}
	y=pass_entry.get_text();
	if (y.length()==0){
		append_status(": Password not specified, switching to default \"guest@guest.com\"\n");
		y="guest@guest.com";
	}
	if (conn.login(x.c_str(),y.c_str())!=0)
		return;
	conn.empty_queue();
	files.clear();
	path="/";
	list();
}


void view::btn_logout(){
	///Logout from current connection.

	if (path=="")	return;
	path="";
	files.clear();
	if (!conn.is_connected()){
		append_status(":ERR - Connection timed out from server\n");
		return;
	}
	conn.empty_queue();
	conn.closeAll();

}

bool view::fls_dbl_click(GdkEvent *ev){
	///event - double click on file structure
	///	     - changes current directory

	if ( ev->button.type == GDK_2BUTTON_PRESS ){
		if (path=="")	return false;
		Glib::RefPtr<Gtk::TreeSelection> ref = files.get_selection();
		Gtk::TreeModel::iterator iter = ref->get_selected();
		Glib::ustring x;
		Gtk::TreeModel::Row row;
		int begin,end;
		char type;

		if(iter){
			row = *iter;
			x=row[files.type];
			type=x[0];
			x=row[files.name];

			switch (type){
			case 'd':
			case 'm':
				if (conn.send("CWD",x.c_str())!=0)
					return false;
				files.clear();
				break;
			case '~':
				if (conn.send("CDUP","")!=0)
					return false;
				files.clear();
				break;
			case 'f':
				btn_down();
				break;
			}
			if (type!='f'){
				conn.send("PWD","",0);
				x=conn.get_last();
				begin=x.find_first_of("\"",0)+1;
				end=x.find_first_of("\"",begin);
				path=x.substr(begin,end-begin);
				if (path!="/")
					path+="/";
				list();
			}
		}
	}
	return false;
}


void view::btn_refr(){
	///event - on click button refresh
	///		 - refreshes the current directory

	if (path==""){
		Gtk::MessageDialog msg("First connect to server",1,Gtk::MESSAGE_ERROR);
		msg.run();
		return;
	}
	files.clear();
	if (!conn.is_connected()){
		append_status(":ERR - Connection timed out from server\n");
		return;
	}
	list();
}

void view::btn_open(){
	string file=choose_file(1);
	if (file.length()>0){
		clear_status();
		if (conn.open_from_xml(file)!=0){
			Gtk::MessageDialog msg("Unknown Error",1,Gtk::MESSAGE_ERROR);
			msg.run();
			return;
		}
		files.clear();
		path="/";
		list();
		server_entry.set_text(conn.get_address());
		user_entry.set_text(conn.get_c_user());
		pass_entry.set_text(conn.get_c_pass());
		port_entry.set_text(Glib::ustring::format(conn.get_c_port()));
		Gtk::MessageDialog msg("Opened successfully! Press Download All when ready to restart your saved download",1,Gtk::MESSAGE_INFO);
		msg.run();
	}
}

void view::btn_about_server(){
	///event - on click button about server
	///		 - displays information about server

	if (path==""){
		Gtk::MessageDialog msg("First connect to server",1,Gtk::MESSAGE_ERROR);
		msg.run();
		return;
	}
	if (!conn.is_connected()){
		append_status(":ERR - Connection timed out from server\n");
		return;
	}
	string x;
	about_server_buff->set_text("");
	x=conn.get_address()+"\n\n";
	x+="Resolved Address: " + conn.get_server_address() + "\n\n";
	x+="Welcome Message:\n"+conn.get_welcome()+"\n\n";
	conn.send("SYST","");
	x+="System type: " + conn.get_last()+"\n\n";
	conn.send("HELP","");
	x+="Help message: \n" + conn.get_last()+"\n\n";

	about_server_buff->set_text(x);
	about_server_dialog.show_all();
}

void view::btn_about(){
	///event - on click button about
	///		 - displays information about application

	about.show();
}

void view::btn_info_yes(){
	///event - on click button yes from info Gtk::InfoBar
	///		 - continues the recursive download

	info.hide();
	btn_load();
}

void view::btn_info_no(){
	///event - on click button no from info Gtk::InfoBar
	///		 - stops the recursive download and clears the queue

	info.hide();
	prog.hide();
	conn.empty_queue();
}

void view::btn_info_save(){
	string file=choose_file(0);
	if (file.length()>0){
		append_status("Saving to file: "+file+".xml ");
		conn.dump_to_xml(file+".xml");
		info.hide();
		prog.hide();
		append_status("--> OK \n");
	}
}

string view::choose_file(int t){
	Gtk::FileChooserAction action;
	if (t==0){
		action=Gtk::FILE_CHOOSER_ACTION_SAVE;
	}
	if (t==1){
		action=Gtk::FILE_CHOOSER_ACTION_OPEN;
	}


	Gtk::FileChooserDialog dialog("Please choose a file",action);

	dialog.set_transient_for(*this);

	//Add response buttons the the dialog:
	dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
	if (t==0)
		dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
	if (t==1)
		dialog.add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK);

	//Add filters, so that only certain file types can be selected:

	Glib::RefPtr<Gtk::FileFilter> filter_any = Gtk::FileFilter::create();
	filter_any->set_name("Any files");
	filter_any->add_pattern("*");
	dialog.add_filter(filter_any);

	//Show the dialog and wait for a user response:
	int result = dialog.run();

	//Handle the response:
	switch(result)
	{
	case(Gtk::RESPONSE_OK):
	    			{

		//Notice that this is a std::string, not a Glib::ustring.
		return dialog.get_filename();
		break;
	    			}
	case(Gtk::RESPONSE_CANCEL):
	    			{
		return "";
		break;
	    			}
	default:
	{
		return "";
		break;
	}
	}
	return "";
}



void view::btn_prog_ok(){
	///event - on click button ok from progress dialog
	///		 - hides the dialog

	prog.hide();
}
void view::btn_prog_cancel(){
	///event - on click button cancel from progress dialog
	///		 - cancels the current operation

	conn.cancel();
}
//=============================================================================================
//View notifiers and updaters =================================================================
void view::list(){
	///Lists current directory and fills the view

	char data_mode=passive_radio.get_active()?'P':'A';
	if (conn.send_receive(data_mode,0,"LIST","","fl.out",'A')!=0)
		return;
	files.fill_list("fl.out",path);
}

void view::append_status(string stat){
	///append string to current status view

	Glib::ustring x=stat;
	Gtk::TextBuffer::iterator it=st_buff->end();

	st_buff->place_cursor(it);
	st_buff->insert_at_cursor(x);
	status.scroll_to(st_buff->get_insert());
	while(Gtk::Main::events_pending())
		Gtk::Main::iteration(false);
}
void view::clear_status(){
	///clear all from status view

	st_buff->set_text("");
}

void view::pulse_progress(long long time,int speed,long long eta){
	///Increase progress with 1% and update info (see model)

	GTimeVal t;
	Glib::ustring u;
	int begin;

	if (time!=-1){
		progress.set_fraction(progress.get_fraction()+0.01);

		t.tv_sec=eta;
		t.tv_usec=0;
		u=g_time_val_to_iso8601(&t);
		begin=u.find_first_of('T',0);
		prog_eta_lbl.set_text("Estimated time: "+u.substr(begin+1,u.length()-begin-2));

		t.tv_sec=time;
		u=g_time_val_to_iso8601(&t);
		begin=u.find_first_of('T',0);
		prog_time_lbl.set_text("Elapsed time: "+u.substr(begin+1,u.length()-begin-2));

		prog_speed_lbl.set_text("Downloading speed: "+Glib::ustring::format(speed)+" KB/s");
	}
	if (time==-1){
		progress.set_fraction(1);
	}
	while(Gtk::Main::events_pending())
		Gtk::Main::iteration(false);
}
void view::reset_progress(){
	///reset progress bar

	progress.set_fraction(0.0);
	while(Gtk::Main::events_pending())
		Gtk::Main::iteration(false);
}
void view::file_progress(string file){
	///set progress current downloading file

	prog_file_lbl.set_text("Downloading file: "+file);
}
//============================================================================================
//Drawing / Dialogs / Showing =================================================================
void view::show_view(){
	///Shows and hides specific elements of the view

	show_all();
	prog.show_all_children(1);
	info.hide();
}

void view::connect_signals(){
	///Connects signals
	Gtk::Button *it=(Gtk::Button*)*(--about.get_action_area()->get_children().end());

	it->signal_clicked().connect(sigc::mem_fun(*this,&view::btn_close_about));
	files.signal_event().connect(sigc::mem_fun(*this,&view::fls_dbl_click));

	prog_ok_btn.signal_clicked().connect(sigc::mem_fun(*this,&view::btn_prog_ok));
	prog_cancel_btn.signal_clicked().connect(sigc::mem_fun(*this,&view::btn_prog_cancel));

	info_yes_btn.signal_clicked().connect(sigc::mem_fun(*this,&view::btn_info_yes));
	info_no_btn.signal_clicked().connect(sigc::mem_fun(*this,&view::btn_info_no));
	info_save_btn.signal_clicked().connect(sigc::mem_fun(*this,&view::btn_info_save));

	about_server_btn.signal_clicked().connect(sigc::mem_fun(*this,&view::btn_close_about_server));

}

void view::draw_toolbar(){
	///Draws toolbar

	action_group = Gtk::ActionGroup::create();

	action_group->add(Gtk::Action::create("Download",
			Gtk::Stock::GO_DOWN, "_Download", "Download the selected file"),
			sigc::mem_fun(*this, &view::btn_down));
	action_group->add(Gtk::Action::create("Refresh",
			Gtk::Stock::REFRESH, "_Refresh", "Refresh the list"),
			sigc::mem_fun(*this, &view::btn_refr));
	action_group->add(Gtk::Action::create("Connect",
			Gtk::Stock::CONNECT, "_Connect", "Connect to ftp server"),
			sigc::mem_fun(*this, &view::btn_connect));
	action_group->add(Gtk::Action::create("Disconnect",
			Gtk::Stock::DISCONNECT, "_Disconnect", "Disconnect from server"),
			sigc::mem_fun(*this, &view::btn_logout));
	action_group->add(Gtk::Action::create("DownloadAll",
			Gtk::Stock::GOTO_BOTTOM, "_DownloadAll", "Download all files recursively starting from current directory"),
			sigc::mem_fun(*this, &view::btn_load));
	action_group->add(Gtk::Action::create("About",
			Gtk::Stock::ABOUT, "_About", "Information about application"),
			sigc::mem_fun(*this, &view::btn_about));
	action_group->add(Gtk::Action::create("AboutServer",
			Gtk::Stock::NETWORK, "_AboutServer", "Information about server"),
			sigc::mem_fun(*this, &view::btn_about_server));
	action_group->add(Gtk::Action::create("Open",
			Gtk::Stock::OPEN, "_Open", "Open file to continue recursive download"),
			sigc::mem_fun(*this, &view::btn_open));
	ui_manager = Gtk::UIManager::create();
	ui_manager->insert_action_group(action_group);

	add_accel_group(ui_manager->get_accel_group());
	//Layout the actions in toolbar:
	Glib::ustring ui_info =
			"<ui>"
			"  <toolbar  name='ToolBar'>"
			"    <toolitem action='Download'/>"
			"    <toolitem action='Open'/>"
			"    <toolitem action='Connect'/>"
			"    <toolitem action='Disconnect'/>"
			"    <toolitem action='DownloadAll'/>"
			"    <toolitem action='Refresh'/>"
			"    <toolitem action='About'/>"
			"	 <toolitem action='AboutServer'/>"
			"  </toolbar>"
			"</ui>";

	ui_manager->add_ui_from_string(ui_info);
	toolbar = ui_manager->get_widget("/ToolBar") ;
	main_box.pack_start(info,0,0,0);
}

void view::prep_view(){
	///Prepare the view by making necessary settings.

	std::vector<Glib::ustring> v;
	v.push_back("Chirila Alexandru");

	//Tooltips
	normal_list_radio.set_tooltip_text("Most common listing mode, not safe and slow, "
			"\nbut implemented by all servers");
	machine_list_radio.set_tooltip_text("More safer and  faster listing mode, "
			"\nbut not implemented by all servers");

	active_radio.set_tooltip_text("Active transfer mode, implies that this aplication selects "
			"\nthe port on which the server connects");
	passive_radio.set_tooltip_text("Passive transfer mode, implies that the server choses "
			"\nthe port on which the aplication connects");

	port_entry.set_tooltip_text("Port for the control connection, ussualy 21");
	user_entry.set_tooltip_text("Leave blank for anonymous");
	pass_entry.set_tooltip_text("Leave blank for anonymous");

	refr_frame.set_tooltip_text("Time out function for checking MDTM of "
			"allready downloaded files (seconds)");
	time_frame.set_tooltip_text("Time out between automatic downloads (miliseconds)");

	//About dialog
	about.set_name("Mirror FTP v.1.0");
	about.set_authors(v);
	about.set_license(get_gnu_license());
	about.set_website("http://students.info.uaic.ro/~alexandru.chirila");
	about.set_program_name("Mirror FTP");
	about.set_copyright("Copyright (C) 2012 Chirila Alexandru");
	about.set_default_response(Gtk::RESPONSE_CLOSE);
	about.set_resizable(true);
	about.set_deletable(true);
	about.set_wrap_license(true);

	//window properties
	set_title ("MirrorFTP");
	set_border_width(10);
	set_default_size(800,600 );

	//other properties

	//padded
	main_vert.set_position(240);
	main_horz.set_position(400);



	//text view
	about_server.set_editable(false);
	about_server_buff=Gtk::TextBuffer::create();
	about_server.set_buffer(about_server_buff);
	about_server.set_wrap_mode(Gtk::WRAP_WORD);
	about_server.set_cursor_visible(false);

	status.set_editable(false);
	st_buff=Gtk::TextBuffer::create();
	status.set_wrap_mode(Gtk::WRAP_WORD);
	status.set_buffer(st_buff);
	status.override_background_color(Gdk::RGBA("Black"));
	status.override_color(Gdk::RGBA("Green"));
	status.override_cursor(Gdk::RGBA("Orange"),Gdk::RGBA("Orange"));
	//status.set_cursor_visible(false);
	st_buff->set_text("Welcome to Mirror FTP, enter the server address and connect to begin");

	//dialog
	about_server_dialog.set_default_size(400,500);
	about_server_dialog.set_title("About Server");

	prog.set_default_size(500,100);
	prog.set_deletable(0);
	progress.set_pulse_step(0.25);
	progress.set_show_text(true);
	prog.set_deletable(false);
	prog.set_title("Downloading");



	//frames
	host_frame.set_label("Host");
	data_frame.set_label("Data connection");
	refr_frame.set_label("Periodic Refresh Rate");
	time_frame.set_label("Time Out");
	list_frame.set_label("Listing type");

	//boxes

	//entry
	server_entry.set_width_chars(20);
	port_entry.set_width_chars(2);
	user_entry.set_width_chars(20);
	pass_entry.set_width_chars(20);
	port_entry.set_text("21");
	server_entry.set_text("");
	user_entry.set_text("");
	pass_entry.set_visibility(false);
	pass_entry.set_invisible_char('*');
}

void view::draw_dialogs(){
	///Draws dialogs.

	Gtk::Box *action,*content;

	//About server
	content=(Gtk::Box*)about_server_dialog.get_content_area();
	action=(Gtk::Box*)about_server_dialog.get_action_area();

	action->pack_start(about_server_btn,0,0,0);

	content->pack_start(about_server_scroll);
	about_server_scroll.add(about_server);

	//InfoBar
	content=(Gtk::Box*)info.get_content_area();
	action=(Gtk::Box*)info.get_action_area();

	action->pack_start(info_yes_btn,0,0,0);
	action->pack_start(info_no_btn,0,0,0);
	action->pack_start(info_save_btn,0,0,0);

	content->pack_start(info_msg_lbl,0,0,0);

	//Progress dialog

	content=prog.get_content_area();
	action=prog.get_action_area();

	action->pack_start(prog_ok_btn);
	action->pack_end(prog_cancel_btn);

	content->pack_start(prog_file_lbl,0,0);
	content->pack_start(prog_time_lbl,0,0);
	content->pack_start(prog_eta_lbl,0,0);
	content->pack_start(prog_speed_lbl,0,0);
	content->pack_end(progress,1,1);
}

void view::draw_view(){
	///Draw the view.

	//grouping
	data_group=active_radio.get_group();
	passive_radio.set_group(data_group);

	list_group=normal_list_radio.get_group();
	machine_list_radio.set_group(list_group);

	//adding

	add(main_box);
	main_box.pack_start(main_horz);
	main_horz.add1(main_vert);

	//file srtucture
	main_vert.add1(ctrl_box);
	fl_scroll.add(files);

	//status text
	main_vert.add2(fl_scroll);
	st_scroll.add(status);


	main_horz.add2(st_scroll);

	//right control box
	ctrl_box.pack_start(*toolbar, Gtk::PACK_SHRINK);
	ctrl_box.pack_start(host_frame,false,false);
	host_frame.add(host_box);

	//host box
	host_box.pack_start(h_lbl,false,false);
	host_box.pack_start(server_box,false,false);
	server_box.pack_start(server_entry);
	server_box.pack_start(port_entry);
	host_box.pack_start(u_lbl,false,false);
	host_box.pack_start(user_entry,false,false);
	host_box.pack_start(p_lbl,false,false);
	host_box.pack_start(pass_entry,false,false);

	ctrl_box.pack_start(data_frame,false,false);
	data_frame.add(data_box);

	//data box
	data_box.pack_start(active_radio,false,false);
	data_box.pack_start(passive_radio,false,false);

	ctrl_box.pack_start(refr_frame,false,false);
	refr_frame.add(refr_box);
	refr_box.pack_start(refr_rate_btn);

	ctrl_box.pack_start(time_frame,false,false);
	time_frame.add(time_out_btn);

	ctrl_box.pack_start(list_frame,false,false);
	list_frame.add(list_box);

	list_box.pack_start(normal_list_radio);
	list_box.pack_start(machine_list_radio);

}
//============================================================================================
